#include "DistCaPClient.hpp"

DistCaPClient::DistCaPClient(GAmodSettings &settings) throw(DistCaPClientException)
{
    TemplateGAmodObjWrapper<string> *strsett;
    const char *tmp;

	pop = NULL;

    strsett = (TemplateGAmodObjWrapper<string>*)settings.get_setting("local_port");
    tmp = (strsett) ? strsett->value.c_str() : NULL;

    if(!tmp)
        throw DistCaPClientException("DistCaPClient::DistCaPClient: specificare una porta di ascolto con 'local_port'.");
	else
	{
		my_port = atoi(tmp);
		if(my_port<=0)
			throw DistCaPClientException("DistCaPClient::DistCaPClient: numero di porta non valido.");
	}
	IF_DBG_VERB(printf("[INFO] DistCaPClient::DistCaPClient: PORT = %d\n",my_port);)

	mySettings.clear();

}

void DistCaPClient::startClient() throw(DistCaPClientException)
{
	int s,addrlen;
	struct sockaddr_in sin,addr;

	s = socket(AF_INET, SOCK_STREAM, 0);
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_port = htons(my_port);

	if(bind (s, (sockaddr*)&sin, sizeof(sin))==-1)
		throw DistCaPClientException("DistCaPClient::startClient: impossibile effettuare il bind col la porta");

	if(listen(s, 0)==-1)
		throw DistCaPClientException("DistCaPClient::startClient: impossibile effettuare listen()");

	//printf("Attendo...\n");
	IF_DBG_VERB(printf("[INFO] DistCaPClient::startClient: Attendo connessione su %d\n",my_port);)
	my_socket = accept(s,(sockaddr*)&addr,(socklen_t*)&addrlen);
	IF_DBG_VERB(printf("[INFO] DistCaPClient::startClient: Connessione accettata avvio handshake...\n");)
	close(s);

	proto = new DistCaPProtocol(my_socket);
	if(proto->waitHandshake(mySettings)==0)
		throw DistCaPClientException("DistCaPClient::startClient: handshake fallito, parametri non corrispondenti");

	try
	{
		myValidator = new CaP_Validator(mySettings);
	}
	catch(EHW_Exception &e)
	{
		throw(e);
	}


	IF_DBG_VERB(printf("[INFO] DistCaPClient::startClient: Handshake OK!\n");)

	try {
		mainClientAction();
	}
	catch (DistCaPClientException &e) {
		throw e;
	}
}

int DistCaPClient::evalPhase(int popSize) throw(DistCaPClientException)
{
	IF_DBG_VERB(printf(".");)
	clock_t t0,te;

	if(!pop)
		try {
			pop = new Popolazione(popSize);
		}
		catch (PopolazioneException &e) {
			proto->sendConfirm(0); //Ecc. non gestita
			std::string errmsg("DistCaPClient::evalPhase:\n\t");
			errmsg = errmsg + e.msg;
			throw DistCaPClientException((char*)errmsg.c_str());
		}
	printf("INVIO CONFERMA 1\n");
	proto->sendConfirm(1); //Ecc. non gestita

	t0 = clock();

	try {
		printf("PROVO A LEGGERE LA POPOLAZIONE...\n");
		pop->load_from_file(proto->my_socket,Popolazione::LOAD_RES_POLICY_RESIZE, 1);
		printf("POPOLAZIONE LETTA\n");
		//pop->print_all();
		//getchar();
	}
	catch (PopolazioneException &e) {
		proto->sendConfirm(0); //Ecc. non gestita
		std::string errmsg("DistCaPClient::evalPhase:\n\t");
		errmsg = errmsg + e.msg;
		throw DistCaPClientException((char*)errmsg.c_str());
	}

	printf("CONFERMO POPOLAZIONE LETTA\n");
	proto->sendConfirm(1); //Ecc. non gestita

	try {
		printf("AVVIO LA VALUTAZIONE\n");
		myValidator->valuta_pop(pop,0,0);
		printf("VALUTAZIONE CONCLUSA\n");
	}
	catch (ValidatorException) {
		//TODO: gestire errori di valutazione
		IF_DBG_INFO(printf("[ERROR] DistCaPClient::evalPhase: Errore in valutazione!\n");)
	}

	try {
		printf("INVIO LE VALUTAZIONI\n");
		proto->sendEvals(pop);
		printf("VALUTAZIONI INVIATE\nATTENDO CONFERMA\n");
		if(proto->waitConfirm()==0)
			IF_DBG_INFO(printf("[ERROR] DistCaPClient::evalPhase: Missing ACK\n");)
		printf("CONFERMA OK\n");
	}
	catch (DistCaPProtocolException &e) {
		//TODO: gestire eccezioni
		IF_DBG_INFO(printf("[ERROR] DistCaPClient::evalPhase: Errore in invio valutazioni!\n");)
	}

	te = clock();

	printf("FINE FASE DI VALUTAZIONE[%.2f]\n",(te-t0)/(float)CLOCKS_PER_SEC);
	return 0;
}

void DistCaPClient::mainClientAction() throw(DistCaPClientException)
{
	StandardPacket sp;
	int run = 1;
	IF_DBG_VERB(printf("[INFO] DistCaPClient::mainClientAction: Avvio mainClientAction\n");)
	while(run == 1)
	{
		try {
			proto->waitPacket(sp);
		}
		catch(DistCaPProtocolException e){
			std::string errmsg("DistCaPClient::mainClientAction:\n\t");
			errmsg = errmsg + e.msg;
			throw DistCaPClientException((char*)errmsg.c_str());
		}
		switch(sp.P_Type)
		{
			case PK_TYPE_END:
				run = 0;
				break;

			case PK_TYPE_EVAL:
				try {
					evalPhase(sp.payload.IntPayload);
				}
				catch (DistCaPClientException &e) {

					throw e;
				}
				break;
		}
	}
	IF_DBG_VERB(printf("[INFO] DistCaPClient::mainClientAction: Avvio procedura di shutdown.\n");)
	sp.P_Type = PK_TYPE_END;
	proto->sendPacket(sp); //Gestire eccezione
	proto->waitConfirm();
	usleep(100000);
	close(proto->my_socket);

	IF_DBG_VERB(printf("[INFO] DistCaPClient::mainClientAction: Shutdown completato.\n");)

}
